﻿using System;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;

using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using FluentValidation.Mvc;
using Nop.Core;
using Nop.Core.Data;
using Nop.Core.Domain;
using Nop.Core.Domain.Common;
using Nop.Core.Infrastructure;
using Nop.Core.Infrastructure.DependencyManagement;
using Nop.Services.Logging;
using Nop.Services.Tasks;
using Nop.Web.Framework;
using Nop.Web.Framework.Mvc;
using Nop.Web.Framework.Mvc.Routes;
using Nop.Web.Framework.Themes;
using StackExchange.Profiling;


using System.Diagnostics;

namespace Nop.Web
{
    public class MvcApplication : System.Web.HttpApplication
    {
        /// <summary>
        /// 注册系统的Filter集合
        /// 在MVC3中   所谓的Filter  是在执行Action的时候 附加的属性 Attribute，用来实现AOP切面编程
        /// Filter继承于ActionFilterAttribute抽象类,并可以覆写 void OnActionExecuting(ActionExecutingContext) 和 void OnActionExecuted(ActionExecutedContext) 以及 void OnResultExecuting(ResultExecutingContext)和 void OnResultExecuted(ResultExecutedContext)。
        /// </summary>
        /// <param name="filters"></param>
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            //do not register HandleErrorAttribute. use classic error handling mode
            //filters.Add(new HandleErrorAttribute());
        }

        /// <summary>
        /// 注册路由
        /// </summary>
        /// <param name="routes"></param>
        public static void RegisterRoutes(RouteCollection routes)
        {
            ///请求的路由中忽略对下面两种类型 的请求
            routes.IgnoreRoute("favicon.ico");
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            //register custom routes (plugins, etc) 注册插件中的路由启动项
            //register custom routes (plugins, etc)
            var routePublisher = EngineContext.Current.Resolve<IRoutePublisher>();
            routePublisher.RegisterRoutes(routes);

            //向系统中注册默认的启动路由  地址
            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                new[] { "Nop.Web.Controllers" }//当前Web控制器 路由对象所在的类的命名空间 Namespace
            );

            ///注意：MVC中的核心 控制器Controller    在模式中提供对模型Model和视图View的转接工作
            ///NOP的Web层面 所有的控制器均限制使用此命名空间，其他插件Pluin 或者Area 中的控制器均类似业务控件
            ///用来生成特定的HTML  
        }

        protected void Application_Start()
        {

            var watch = new System.Diagnostics.Stopwatch();
            var costTime = "";
            watch.Start();

            //initialize engine context 
            //初始化引擎 并设置NOP系统的依赖注册  IOC的容器配置已经MVC的依赖注入
            //注意 ：MVCD 的依赖注入容器   不再使用原来的 实例管理工具NopDependencyResolver 转而使用Autofac内置的实例化工具
            //在NopEngine中的RegisterDependencies时候 进行依赖注入DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
            EngineContext.Initialize(false);



            //model binders
            //模型绑定器 
            ModelBinders.Binders.Add(typeof(BaseNopModel), new NopModelBinder());



            ///是否已经安装完毕数据库
            bool databaseInstalled = DataSettingsHelper.DatabaseIsInstalled();
            if (databaseInstalled)
            {
                //毫无悬念 NOP重新写了MVC中的视图引擎 ，基于Razor的更高级的扩展，来适应多变的主题皮肤  Widget
                //remove all view engines
                ViewEngines.Engines.Clear();
                //except the themeable razor view engine we use
                ViewEngines.Engines.Add(new ThemeableRazorViewEngine());
            }


            //Add some functionality on top of the default ModelMetadataProvider
            ModelMetadataProviders.Current = new NopMetadataProvider();

            //Registering some regular mvc stuff  注册所有的Area  
            //在MVC   Areas是将ASP.NET MVC应用按照不同的功能模块划分，对每个功能模块使用ASP.NET MVC规则的目录结构和命名方法
            AreaRegistration.RegisterAllAreas();

            ///注册路由
            RegisterRoutes(RouteTable.Routes);




            //fluent validation   启用客户端 数据验证
            DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
            ModelValidatorProviders.Providers.Add(new FluentValidationModelValidatorProvider(new NopValidatorFactory()));

            //start scheduled tasks  开始任务调度
            if (databaseInstalled)
            {

                //TaskManager.Instance.Initialize();
                //TaskManager.Instance.Start();

                //由于NOP系统启动的时候 所有的任务都是基于定时触发的 ，
                //所以 没有必要保存阻塞当前线程，执行异步直接将主线程释放给当前
                System.Threading.Tasks.Task.Factory.StartNew(
                                () =>
                                {
                                    TaskManager.Instance.Initialize();
                                    TaskManager.Instance.Start();
                                }
                    );

            }

            //log application start
            if (databaseInstalled)
            {
                try
                {
                    //log  -------------记录应用程序开启
                    var logger = EngineContext.Current.Resolve<ILogger>();
                    logger.Information("Application started", null, null);
                }
                catch (Exception)
                {
                    //don't throw new exception if occurs
                }
            }

            //****************************启动任务耗时************************************
            //watch.Stop();
            //costTime = "";
            //costTime = string.Format("启动任务耗时耗时{0}毫秒！", watch.ElapsedMilliseconds.ToString());
            //Debug.WriteLine(costTime);
            //****************************************************************

            ///启动完毕
        }


        /// <summary>
        /// 接受Web请求
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Application_BeginRequest(object sender, EventArgs e)
        {
            //ignore static resources
            var webHelper = EngineContext.Current.Resolve<IWebHelper>();
            //不处理静态资源  jpg  css js 等
            if (webHelper.IsStaticResource(this.Request))
                return;

            //keep alive page requested (we ignore it to prevent creating a guest customer records)
            string keepAliveUrl = string.Format("{0}keepalive/index", webHelper.GetStoreLocation());
            if (webHelper.GetThisPageUrl(false).StartsWith(keepAliveUrl, StringComparison.InvariantCultureIgnoreCase))
                return;

            //ensure database is installed
            if (!DataSettingsHelper.DatabaseIsInstalled())
            {
                //如果数据库没有安装  那么进入安装页面
                string installUrl = string.Format("{0}install", webHelper.GetStoreLocation());
                if (!webHelper.GetThisPageUrl(false).StartsWith(installUrl, StringComparison.InvariantCultureIgnoreCase))
                {
                    this.Response.Redirect(installUrl);
                }
            }

            if (!DataSettingsHelper.DatabaseIsInstalled())
                return;

            //miniprofiler   调试器
            if (EngineContext.Current.Resolve<StoreInformationSettings>().DisplayMiniProfilerInPublicStore)
            {
                MiniProfiler.Start();
                //store a value indicating whether profiler was started
                HttpContext.Current.Items["nop.MiniProfilerStarted"] = true;
            }
        }


        /// <summary>
        /// 处理完毕请求
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Application_EndRequest(object sender, EventArgs e)
        {
            //miniprofiler
            var miniProfilerStarted = HttpContext.Current.Items.Contains("nop.MiniProfilerStarted") &&
                 Convert.ToBoolean(HttpContext.Current.Items["nop.MiniProfilerStarted"]);
            if (miniProfilerStarted)
            {
                MiniProfiler.Stop();
            }
        }

        protected void Application_AuthenticateRequest(object sender, EventArgs e)
        {
            //we don't do it in Application_BeginRequest because a user is not authenticated yet
            SetWorkingCulture();
        }


        /// <summary>
        /// 获取所有运行错误 记录到日志中 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Application_Error(Object sender, EventArgs e)
        {
            var exception = Server.GetLastError();

            //log error
            LogException(exception);

            //process 404 HTTP errors
            var httpException = exception as HttpException;
            if (httpException != null && httpException.GetHttpCode() == 404)
            {
                var webHelper = EngineContext.Current.Resolve<IWebHelper>();
                if (!webHelper.IsStaticResource(this.Request))
                {
                    Response.Clear();
                    Server.ClearError();
                    Response.TrySkipIisCustomErrors = true;

                    //在MVC中  执行页面跳转的功能 无非是加载指定的控制器  然后执行其中的Action
                    //此功能类似ASP.NET的WebForm中的Server.Transfer()  微软的解释 是执行指定的请求上下文
                    //此执行 是控制器继承底层的最核心的方法  用来知心一个控制器实例 中的接受到的请求上下文
                    // Call target Controller and pass the routeData./// 将应用程序的错误信息  显示到系统中的错误页面
                    IController errorController = EngineContext.Current.Resolve<Nop.Web.Controllers.CommonController>();

                    var routeData = new RouteData();
                    routeData.Values.Add("controller", "Common");
                    routeData.Values.Add("action", "PageNotFound");

                    errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
                }
            }
        }

        /// <summary>
        /// 设置当前程序的线程环境 中文还是英文 还是其他国家
        /// </summary>
        protected void SetWorkingCulture()
        {
            if (!DataSettingsHelper.DatabaseIsInstalled())
                return;

            //ignore static resources  忽略静态资源
            var webHelper = EngineContext.Current.Resolve<IWebHelper>();
            if (webHelper.IsStaticResource(this.Request))
                return;

            //keep alive page requested (we ignore it to prevent creation of guest customer records)保持当前Web所在的应用程序连接池不被回收掉
            //一旦被回收  那么就会引起对web的预编译
            string keepAliveUrl = string.Format("{0}keepalive/index", webHelper.GetStoreLocation());
            if (webHelper.GetThisPageUrl(false).StartsWith(keepAliveUrl, StringComparison.InvariantCultureIgnoreCase))
                return;


            if (webHelper.GetThisPageUrl(false).StartsWith(string.Format("{0}admin", webHelper.GetStoreLocation()),
                StringComparison.InvariantCultureIgnoreCase))
            {
                //admin area


                //always set culture to 'en-US'
                //we set culture of admin area to 'en-US' because current implementation of Telerik grid 
                //doesn't work well in other cultures
                //e.g., editing decimal value in russian culture
                CommonHelper.SetTelerikCulture();
            }
            else
            {
                //public store
                var workContext = EngineContext.Current.Resolve<IWorkContext>();
                var culture = new CultureInfo(workContext.WorkingLanguage.LanguageCulture);
                Thread.CurrentThread.CurrentCulture = culture;
                Thread.CurrentThread.CurrentUICulture = culture;
            }
        }

        /// <summary>
        /// 记录运行时的错误异常信息到日志中
        /// </summary>
        /// <param name="exc"></param>
        protected void LogException(Exception exc)
        {
            if (exc == null)
                return;

            if (!DataSettingsHelper.DatabaseIsInstalled())
                return;

            //ignore 404 HTTP errors
            var httpException = exc as HttpException;
            if (httpException != null && httpException.GetHttpCode() == 404 &&
                !EngineContext.Current.Resolve<CommonSettings>().Log404Errors)
                return;

            try
            {
                //log
                var logger = EngineContext.Current.Resolve<ILogger>();
                var workContext = EngineContext.Current.Resolve<IWorkContext>();
                logger.Error(exc.Message, exc, workContext.CurrentCustomer);
            }
            catch (Exception)
            {
                //don't throw new exception if occurs
            }
        }
    }
}